﻿//==============================================================================
// File: OneToManyAssocSync.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections;
using System.Collections.Specialized;

namespace WpfRcp.PropertyModel.Internal.Associations
{
    /// <summary>
    /// Keeps both sides of a bidirectional one-to-many association in sync with each other.
    /// </summary>
    internal static class OneToManyAssocSync
    {
        /// <summary>
        /// Resynchronizes collection from one-to-many side with many-to-one side.
        /// </summary>
        public static void ResynchronizeManySide(PropertyContainer oneSide, MetaProperty oneToManyProperty, MetaProperty manyToOneProperty, object oneToManyCollection)
        {
            if (oneToManyCollection != null && !oneSide.IsSynchronizing(oneToManyProperty))
            {
                var oneToManyList = (IList)oneToManyCollection;
                using (oneSide.BlockSynchronizationReentrency(oneToManyProperty))
                {
                    foreach (PropertyContainer addingToManySide in oneToManyList)
                    {
                        if (addingToManySide.GetValue(manyToOneProperty) != oneSide)
                        {
                            addingToManySide.SetValue(manyToOneProperty, oneSide);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Unsynchronizes collection from one-to-many side with many-to-one side (setting null values).
        /// </summary>
        public static void UnsynchronizeManySide(PropertyContainer oneSide, MetaProperty oneToManyProperty, MetaProperty manyToOneProperty, object oneToManyCollection)
        {
            if (oneToManyCollection != null && !oneSide.IsSynchronizing(oneToManyProperty))
            {
                var oneToManyList = (IList)oneToManyCollection;
                using (oneSide.BlockSynchronizationReentrency(oneToManyProperty))
                {
                    foreach (PropertyContainer addingToManySide in oneToManyList)
                    {
                        if (addingToManySide.GetValue(manyToOneProperty) != oneSide)
                        {
                            addingToManySide.SetValue(manyToOneProperty, null);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Responds to add/remove events raised by the one-side's collection.
        /// </summary>
        public static void UpdateManySide(PropertyContainer oneSide, MetaProperty oneToManyProperty, MetaProperty manyToOneProperty, NotifyCollectionChangedEventArgs e)
        {
            if (!oneSide.IsSynchronizing(oneToManyProperty))
            {
                using (oneSide.BlockSynchronizationReentrency(oneToManyProperty))
                {
                    if (e.Action == NotifyCollectionChangedAction.Add)
                    {
                        foreach (object item in e.NewItems)
                        {
                            var addingToManySide = item as PropertyContainer;
                            if (addingToManySide == null)
                                throw new ArgumentException(
                                    "To use OneToMany sychronization other property must be of type that derives from PropertyContainer.");
                            
                            if (addingToManySide.GetValue(manyToOneProperty) != oneSide)
                            {
                                addingToManySide.SetValue(manyToOneProperty, oneSide);
                            }
                        }
                    }
                    else if (e.Action == NotifyCollectionChangedAction.Remove)
                    {
                        foreach (object item in e.OldItems)
                        {
                            var removingFromManySide = item as PropertyContainer;
                            if (removingFromManySide == null)
                                throw new ArgumentException(
                                    "To use OneToMany sychronization other property must be of type that derives PropertyContainer.");

                            if (removingFromManySide.GetValue(manyToOneProperty) == oneSide)
                            {
                                removingFromManySide.SetValue(manyToOneProperty, null);
                            }
                        }
                    }
                    else
                    {
                        throw new NotSupportedException("Handling such collection change is not supported.");
                    }
                }
            }
        }
    }
}